#include <string.h>
#include "aliyun_iot_platform_network.h"
#include "aliyun_iot_common_log.h"
#include "aliyun_iot_platform_memory.h"

#define CANONNAME_MAX 128
extern int errno;


typedef struct OPT_DATA_TRANS
{
    INT32 privateData;
    INT32 systemData;
}OPT_DATA_TRANS_S;

typedef struct NETWORK_ERRNO_TRANS
{
    INT32 systemData;
    ALIYUN_NETWORK_ERROR_E netwokErrno;
    INT32 privateData;
}NETWORK_ERRNO_TRANS_S;

static OPT_DATA_TRANS_S g_optDataTrans[] =
{   
    {SO_REUSEADDR_IOT,SO_REUSEADDR},
    {SO_KEEPALIVE_IOT,SO_KEEPALIVE},
    {SO_BROADCAST_IOT,SO_BROADCAST},
    {SO_RCVTIMEO_IOT,SO_RCVTIMEO},
    {SO_ERROR_IOT,SO_ERROR},
    {SO_TYPE_IOT,SO_TYPE},
    {SO_NO_CHECK_IOT,SO_NO_CHECK},
    {SOL_SOCKET_IOT,SOL_SOCKET},
    {IPPROTO_TCP_IOT,IPPROTO_TCP}
};

static NETWORK_ERRNO_TRANS_S g_networkErrnoTrans[]=
{
    {EINTR,NETWORK_SIGNAL_INTERRUPT,EINTR_IOT},
    {EBADF,NETWORK_BAD_FILEFD,EBADF_IOT},
    {EAGAIN,NETWORK_TRYAGAIN,EAGAIN_IOT},
    {EFAULT,NETWORK_BADADDRESS,EFAULT_IOT},
    {EBUSY,NETWORK_RESOURCE_BUSY, EBUSY_IOT},
    {EINVAL,NETWORK_INVALID_ARGUMENT, EINVAL_IOT},
    {ENFILE,NETWORK_FILE_TABLE_OVERFLOW, ENFILE_IOT},
    {EMFILE,NETWORK_TOO_MANY_OPEN_FILES, EMFILE_IOT},
    {ENOSPC,NETWORK_NO_SPACE_LEFT_ON_DEVICE, ENOSPC_IOT},
    {EPIPE,NETWORK_BROKEN_PIPE, EPIPE_IOT},
    {EWOULDBLOCK,NETWORK_OPERATION_BLOCK, EWOULDBLOCK_IOT},
    {ENOTSOCK,NETWORK_OPERATION_ON_NONSOCKET, ENOTSOCK_IOT},
    {ENOPROTOOPT,NETWORK_PROTOCOL_NOT_AVAILABLE, ENOPROTOOPT_IOT},
    {EADDRINUSE,NETWORK_ADDRESS_ALREADY_IN_USE, EADDRINUSE_IOT},
    {EADDRNOTAVAIL,NETWORK_CANNOT_ASSIGN_REQUESTED_ADDRESS, EADDRNOTAVAIL_IOT},
    {ENETDOWN,NETWORK_NETWORK_IS_DOWN, ENETDOWN_IOT},
    {ENETUNREACH,NETWORK_NETWORK_IS_UNREACHABLE, ENETUNREACH_IOT},
    {ENETRESET,NETWORK_CONNECT_RESET, ENETRESET_IOT},
    {ECONNRESET,NETWORK_CONNECT_RESET_BY_PEER, ECONNRESET_IOT},
    {ENOBUFS,NETWORK_NO_BUFFER_SPACE, ENOBUFS_IOT},
    {EISCONN,NETWORK_ALREADY_CONNECTED, EISCONN_IOT},
    {ENOTCONN,NETWORK_IS_NOT_CONNECTED, ENOTCONN_IOT},
    {ETIMEDOUT,NETWORK_CONNECTION_TIMED_OUT, ETIMEDOUT_IOT},
    {ECONNREFUSED,NETWORK_CONNECTION_REFUSED, ECONNREFUSED_IOT},
    {EHOSTDOWN,NETWORK_HOST_IS_DOWN, EHOSTDOWN_IOT},
    {EHOSTUNREACH,NETWORK_NO_ROUTE_TO_HOST, EHOSTUNREACH_IOT},
    {ENOMEM ,NETWORK_OUT_OF_MEMORY, ENOMEM_IOT},
    {EMSGSIZE,NETWORK_MSG_TOO_LONG, EMSGSIZE_IOT}
};

INT32 errno_transform(INT32 systemErrno,ALIYUN_NETWORK_ERROR_E *netwokErrno,INT32 *privateErrno)
{
    INT32 num = sizeof(g_networkErrnoTrans);
    INT32 i = 0;
    for(i = 0;i<num;i++)
    {
        if(g_networkErrnoTrans[i].systemData == systemErrno)
        {
            *netwokErrno = g_networkErrnoTrans[i].netwokErrno;
            *privateErrno = g_networkErrnoTrans[i].privateData;
            return NETWORK_SUCCESS;
        }
    }

    return NETWORK_FAIL;
}

INT32 optdata_tansform(INT32 privatedata,INT32 *systemData)
{
    INT32 num = sizeof(g_optDataTrans);
    INT32 i = 0;
    for(i = 0;i<num;i++)
    {
        if(g_optDataTrans[i].privateData == privatedata)
        {
            *systemData = g_optDataTrans[i].systemData;
            return NETWORK_SUCCESS;
        }
    }

    return NETWORK_FAIL;
}

INT32 socketaddr_datatype_private2system(ALIYUN_NETWORK_SOCKADDR_IN_S *sockAddr, struct sockaddr_t *saddr)
{
    if(sockAddr->sin_family == AF_INET_IOT || sockAddr->sin_family == PF_INET_IOT)
    {
        saddr->s_type = AF_INET;
    }
    else if(sockAddr->sin_family == AF_UNSPEC_IOT || sockAddr->sin_family == PF_UNSPEC_IOT)
    {
        saddr->s_type = AF_INET;
    }
    else
    {
    	WRITE_IOT_ERROR_LOG("private to system failed");
        return NETWORK_FAIL;
    }

    saddr->s_port = ntohs(sockAddr->sin_port);
    if(sockAddr->sin_addr.s_addr == 0)
    {
        saddr->s_ip = htonl(INADDR_ANY);
    }
    else
    {
        saddr->s_ip = sockAddr->sin_addr.s_addr;
    }

    return NETWORK_SUCCESS;
}

INT32 socketaddr_datatype_system2private(struct sockaddr_t *saddr,ALIYUN_NETWORK_SOCKADDR_IN_S *sockAddr)
{
/*
    if(saddr->s_type == AF_INET)
    {
        sockAddr->sin_family = AF_INET_IOT;
    }
    else
    {
        return NETWORK_FAIL;
    }
*/
	sockAddr->sin_family = AF_INET_IOT;

    sockAddr->sin_port = saddr->s_port;

    sockAddr->sin_addr.s_addr = saddr->s_ip;

    return NETWORK_SUCCESS;
}

INT32 family_socktype_protocol_transform_private2system(INT32 af,INT32 type,INT32 protocol,INT32 *sysaf,INT32 *systype,INT32 *sysprotocol)
{
    if(af == AF_INET_IOT || af == PF_INET_IOT)
    {
        *sysaf = AF_INET;
    }
	else if(af == AF_UNSPEC_IOT || af == PF_UNSPEC_IOT)
    {
        *sysaf = AF_INET;
    }
    else
    {
        return NETWORK_FAIL;
    }

    if(type == SOCK_STREAM_IOT)
    {
        *systype = SOCK_STREAM;
    }
    else if(type == SOCK_DGRAM_IOT)
    {
        *systype = SOCK_DGRM;
    }
    else
    {
        return NETWORK_FAIL;
    }

    switch (protocol)
    {
        case IPPROTO_IP_IOT:
            *sysprotocol = IPPROTO_IP;
            break;
        case IPPROTO_TCP_IOT:
            *sysprotocol = IPPROTO_TCP;
            break;
        case IPPROTO_UDP_IOT:
            *sysprotocol = IPPROTO_UDP;
            break;
        default:
            return NETWORK_FAIL;
    }

    return NETWORK_SUCCESS;
}


INT32 family_socktype_protocol_transform_system2private(INT32 sysaf,INT32 systype,INT32 sysprotocol,INT32 *af,INT32 *type,INT32 *protocol)
{
    if(sysaf == AF_INET)
    {
        *af = AF_INET_IOT;
    }
    else
    {
        return NETWORK_FAIL;
    }

    if(systype == SOCK_STREAM)
    {
        *type = SOCK_STREAM_IOT;
    }
    else if(systype == SOCK_DGRM)
    {
        *type = SOCK_DGRAM_IOT;
    }
    else
    {
        return NETWORK_FAIL;
    }

    switch (sysprotocol)
    {
        case IPPROTO_IP:
            *protocol = IPPROTO_IP_IOT;
            break;
        case IPPROTO_TCP:
            *protocol = IPPROTO_TCP_IOT;
            break;
        case IPPROTO_UDP:
            *protocol = IPPROTO_UDP_IOT;
            break;
        default:
            return NETWORK_FAIL;
    }

    return NETWORK_SUCCESS;
}

INT32 aliyun_iot_get_errno(VOID)
{
    ALIYUN_NETWORK_ERROR_E networkErrno = NETWORK_FAIL;
    INT32 private = 0;
    INT32 result = errno_transform(errno,&networkErrno,&private);
    if(0 != result)
    {
        WRITE_IOT_ERROR_LOG("network errno = %d",errno);
        return NETWORK_FAIL;
    }

    return private;
}

INT32 aliyun_iot_network_socket(INT32 af,INT32 type,INT32 protocol)
{
    INT32 sysaf = 0;
    INT32 systype = 0;
    INT32 sysprotocol = 0;

    if(0 != family_socktype_protocol_transform_private2system(af,type,protocol,&sysaf,&systype,&sysprotocol))
    {
        return NETWORK_PARAM_ERROR;
    }

    return socket(sysaf,systype,sysprotocol);
}

INT32 aliyun_iot_network_bind(INT32 sockFd, ALIYUN_NETWORK_SOCKADDR_IN_S *sockAddr, INT32 addrLen )
{
    struct sockaddr_t saddr;
    memset(&saddr,0x0,sizeof(saddr));

    if(NETWORK_FAIL == socketaddr_datatype_private2system(sockAddr,&saddr))
    {
        return NETWORK_FAIL;
    }

    return bind(sockFd,(struct sockaddr_t *)&saddr,sizeof(saddr));
}

INT32 aliyun_iot_network_listen( INT32 sockFd, INT32 backlog)
{
    return listen(sockFd,backlog);
}

INT32 aliyun_iot_network_connect(INT32 sockfd,ALIYUN_NETWORK_SOCKADDR_IN_S * serv_addr,INT32 addrlen)
{
    struct sockaddr_t saddr;
    memset(&saddr,0x0,sizeof(saddr));

    if(NETWORK_FAIL == socketaddr_datatype_private2system(serv_addr, &saddr))
    {
        return NETWORK_FAIL;
    }

    return connect(sockfd,(struct sockaddr_t*)&saddr,sizeof(saddr));
}

INT32 aliyun_iot_network_send(INT32 sockFd, VOID *buf, INT32 nbytes, UINT32 flags)
{
    switch (flags)
    {
        case MSG_PEEK_IOT:
            flags = 0;
            break;
        case MSG_WAITALL_IOT:
            flags = 0;
            break;
        case MSG_OOB_IOT:
            flags = 0;
            break;
        case MSG_DONTWAIT_IOT:
            flags = 0;
            break;
        case MSG_MORE_IOT:
            flags = 0;
            break;
        default:
            flags = 0;
            break;
    }

    return send(sockFd,buf,nbytes,flags);
}

INT32 aliyun_iot_network_recv(INT32 sockFd, VOID *buf, INT32 nbytes, UINT32 flags)
{
    switch (flags)
    {
        case MSG_PEEK_IOT:
            flags = 0;
            break;
        case MSG_WAITALL_IOT:
            flags = 0;
            break;
        case MSG_OOB_IOT:
            flags = 0;
            break;
        case MSG_DONTWAIT_IOT:
			struct timeval_t timeout;
			timeout.tv_sec = 1;
			timeout.tv_usec = 0;
			
			WRITE_IOT_NOTICE_LOG("MSG_DONTWAIT_IOT");
			setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval_t));
            flags = 0;
            break;
        case MSG_MORE_IOT:
            flags = 0;
            break;
        default:
            flags = 0;
            break;
    }

    return recv(sockFd,buf,nbytes,flags);
}

INT32 aliyun_iot_network_read(INT32 sockFd, VOID *buf, INT32 nbytes)
{
    return read(sockFd,buf,nbytes);
}

INT32 aliyun_iot_network_write(INT32 sockFd, const VOID *buf, INT32 nbytes)
{
    return write(sockFd,(VOID*)buf,nbytes);
}

INT32 aliyun_iot_network_select(INT32 maxFd,ALIYUN_NETWORK_FD_SET_S *readset,ALIYUN_NETWORK_FD_SET_S*writeset,ALIYUN_NETWORK_FD_SET_S*exceptset,ALIYUN_IOT_TIME_TYPE_S * timeout)
{
    fd_set *rd_set = NULL;
    fd_set *wr_set = NULL;
    fd_set *ep_set = NULL;
	
	struct timeval_t tv;
    tv.tv_sec = timeout->time / 1000;
    tv.tv_usec = (timeout->time % 1000) * 1000;

    if(readset == NULL)
    {
        rd_set = NULL;
    }
    else
    {
        rd_set = &readset->sets;
    }

    if(writeset == NULL)
    {
        wr_set = NULL;
    }
    else
    {
        wr_set = &writeset->sets;
    }

    if(exceptset == NULL)
    {
        ep_set = NULL;
    }
    else
    {
        ep_set = &exceptset->sets;
    }

    return select(maxFd,rd_set,wr_set,ep_set,&tv);
}

VOID aliyun_iot_network_FD_SET(INT32 fd,ALIYUN_NETWORK_FD_SET_S *set)
{
    FD_SET(fd,&set->sets);
}

VOID aliyun_iot_network_FD_CLR(INT32 fd,ALIYUN_NETWORK_FD_SET_S *set)
{
    FD_CLR(fd,&set->sets);
}

INT32 aliyun_iot_network_FD_ISSET(INT32 fd,ALIYUN_NETWORK_FD_SET_S *set)
{
    return FD_ISSET(fd,&set->sets);
}

VOID aliyun_iot_network_FD_ZERO(ALIYUN_NETWORK_FD_SET_S* set)
{
    FD_ZERO(&set->sets);
}

INT32 aliyun_iot_network_setsockopt(INT32 fd,INT32 level,INT32 optname,VOID*optval,INT32 optlen)
{
    INT32 systemOptname = 0;
    INT32 systemLevel = 0;
    if(NETWORK_SUCCESS != optdata_tansform(level,&systemLevel))
    {
        return NETWORK_FAIL;
    }

    if(NETWORK_SUCCESS != optdata_tansform(optname,&systemOptname))
    {
        return NETWORK_FAIL;
    }

    if(optname == SO_RCVTIMEO_IOT || optname == SO_SNDTIMEO_IOT)
    {
        optval = (VOID*)&((ALIYUN_IOT_TIME_TYPE_S*)optval)->time;

        optlen = sizeof(struct timeval_t);
    }

    return setsockopt(fd,systemLevel,systemOptname,optval,optlen);
}

INT32 aliyun_iot_network_get_nonblock(INT32 fd)
{
/*
    if( ( fcntl( fd, F_GETFL, 0 ) & O_NONBLOCK ) != O_NONBLOCK )
    {
        return 0;
    }

    if(errno == EAGAIN || errno == EWOULDBLOCK)
    {
        return 1;
    }
*/
    return 0;
}

INT32 aliyun_iot_network_set_nonblock(INT32 fd)
{
/*
    INT32 flags = fcntl(fd, F_GETFL, 0);
    if (fcntl(fd, F_SETFL, (flags | O_NONBLOCK)) < 0)
    {
        return NETWORK_FAIL;
    }
*/
    return NETWORK_SUCCESS;
}

INT32 aliyun_iot_network_set_block(INT32 fd)
{
/*
    INT32 flags = fcntl(fd, F_GETFL, 0);
    if (fcntl(fd, F_SETFL, (flags & (~O_NONBLOCK))) < 0)
    {
        return NETWORK_FAIL;
    }
*/
    return NETWORK_SUCCESS;
}

INT32 aliyun_iot_network_close(INT32 fd)
{
    return close(fd);
}

INT32 aliyun_iot_network_shutdown(INT32 fd,INT32 how)
{
    return close(fd);
}

VOID release_addrinfo_struct(ALIYUN_NETWORK_ADDRINFO_S* addrInfo)
{
    if(addrInfo == NULL)
    {
        return;
    }

    if(addrInfo->ai_addr != NULL)
    {
        aliyun_iot_memory_free(addrInfo->ai_addr);
    }

    if(addrInfo->ai_canonname != NULL)
    {
        aliyun_iot_memory_free(addrInfo->ai_canonname);
    }

    aliyun_iot_memory_free(addrInfo);
}

ALIYUN_NETWORK_ADDRINFO_S* creat_addrinfo_struct()
{
    ALIYUN_NETWORK_ADDRINFO_S* tmp = (ALIYUN_NETWORK_ADDRINFO_S*)aliyun_iot_memory_malloc(sizeof(ALIYUN_NETWORK_ADDRINFO_S));
    if(NULL == tmp)
    {
        return NULL;
    }
    memset(tmp,0x0,sizeof(ALIYUN_NETWORK_ADDRINFO_S));

    tmp->ai_addr = (ALIYUN_NETWORK_SOCKADDR_IN_S*)aliyun_iot_memory_malloc(sizeof(ALIYUN_NETWORK_SOCKADDR_IN_S));
    if(NULL == tmp->ai_addr)
    {
        release_addrinfo_struct(tmp);
        return NULL;
    }
    memset(tmp->ai_addr,0x0,sizeof(ALIYUN_NETWORK_SOCKADDR_IN_S));

    tmp->ai_canonname = (INT8*)aliyun_iot_memory_malloc(sizeof(INT8)*CANONNAME_MAX);
    if(NULL == tmp->ai_canonname)
    {
        release_addrinfo_struct(tmp);
        return NULL;
    }
    memset(tmp->ai_canonname,0x0,sizeof(INT8)*CANONNAME_MAX);

    return tmp;
}


INT32 aliyun_iot_network_getaddrinfo(const INT8*host,const INT8*service,ALIYUN_NETWORK_ADDRINFO_S*hints,ALIYUN_NETWORK_ADDRINFO_S**result)
{
	char ipstr[16] = {0};
	struct sockaddr_t address;
    *result = NULL;

    ALIYUN_NETWORK_ADDRINFO_S* tmp = creat_addrinfo_struct();
    if(NULL == tmp)
    {
        return NETWORK_FAIL;
    }
	
    if(0 != family_socktype_protocol_transform_private2system(hints->ai_family, hints->ai_socktype, hints->ai_protocol, &(tmp->ai_family), &(tmp->ai_socktype), &(tmp->ai_protocol)))
    {
        return NETWORK_PARAM_ERROR;
    }
	
	if(0 != gethostbyname(host, (uint8_t *)ipstr, 16))
	{
		WRITE_IOT_ERROR_LOG("DNS resolve error");
		return NETWORK_FAIL;
	}

	address.s_ip = inet_addr(ipstr);
	address.s_port = htons(atoi(service));

	if(0 != socketaddr_datatype_system2private(&address, tmp->ai_addr))
	{
		return NETWORK_PARAM_ERROR;
	}

	*result = tmp;

    return NETWORK_SUCCESS;
}

VOID aliyun_iot_network_freeaddrinfo(ALIYUN_NETWORK_ADDRINFO_S*result)
{
    ALIYUN_NETWORK_ADDRINFO_S* cur = result;
    ALIYUN_NETWORK_ADDRINFO_S* next = NULL;
    do
    {
        next = cur->ai_next;
        release_addrinfo_struct(cur);
        cur = next;

    }while(cur != NULL);
}



